import sys
import os
from pathlib import Path
import io
import win32com.client
import fitz
from docx import Document
from docx.shared import Inches
from PIL import Image
import pandas as pd
from pptx import Presentation
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                           QPushButton, QFileDialog, QLabel, QComboBox,
                           QProgressBar, QMessageBox, QHBoxLayout, QTextEdit)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from src.utils import Logger

logger = Logger()

class ConversionError(Exception):
    pass

class DocumentConverter:
    SUPPORTED_CONVERSIONS = {
        ('.pdf', '.docx'): 'pdf_to_docx',
        ('.docx', '.pdf'): 'docx_to_pdf',
        ('.xlsx', '.pdf'): 'excel_to_pdf',
        ('.pptx', '.pdf'): 'ppt_to_pdf',
        ('.pdf', '.xlsx'): 'pdf_to_excel',
        ('.pdf', '.pptx'): 'pdf_to_ppt',
        ('.docx', '.xlsx'): 'word_to_excel',
        ('.docx', '.pptx'): 'word_to_ppt',
        ('.xlsx', '.docx'): 'excel_to_word',
        ('.xlsx', '.pptx'): 'excel_to_ppt',
        ('.pptx', '.docx'): 'ppt_to_word',
        ('.pptx', '.xlsx'): 'ppt_to_excel',
    }
    
    def __init__(self):
        self.current_progress = 0
        
    def update_progress(self, value):
        self.current_progress = value
        
    def pdf_to_docx(self, input_file: str, output_file: str) -> bool:
        try:
            doc = Document()
            pdf_document = fitz.open(input_file)
            total_pages = len(pdf_document)
            
            for page_num in range(total_pages):
                self.update_progress(int((page_num + 1) * 100 / total_pages))
                page = pdf_document[page_num]
                
                if page_num > 0:
                    doc.add_page_break()
                
                blocks = page.get_text("blocks")
                blocks.sort(key=lambda b: (b[1], b[0]))
                
                for block in blocks:
                    text = block[4].strip()
                    if not text:
                        continue
                    doc.add_paragraph(text)
                
                for img in page.get_images():
                    try:
                        xref = img[0]
                        base_image = pdf_document.extract_image(xref)
                        image_bytes = base_image["image"]
                        image = Image.open(io.BytesIO(image_bytes))
                        temp_img_path = f"temp_img_{page_num}_{xref}.png"
                        image.save(temp_img_path)
                        doc.add_picture(temp_img_path, width=Inches(6.0))
                        os.remove(temp_img_path)
                    except Exception as e:
                        print(f"图片处理错误: {str(e)}")
            
            doc.save(output_file)
            pdf_document.close()
            return True
        except Exception as e:
            raise ConversionError(f"PDF转Word失败: {str(e)}")

    def docx_to_pdf(self, input_file: str, output_file: str) -> bool:
        try:
            word = win32com.client.Dispatch("Word.Application")
            doc = word.Documents.Open(input_file)
            doc.SaveAs(output_file, FileFormat=17)
            doc.Close()
            word.Quit()
            return True
        except Exception as e:
            raise ConversionError(f"Word转PDF失败: {str(e)}")

    def excel_to_pdf(self, input_file: str, output_file: str) -> bool:
        try:
            excel = win32com.client.Dispatch("Excel.Application")
            wb = excel.Workbooks.Open(input_file)
            wb.ExportAsFixedFormat(0, output_file)
            wb.Close()
            excel.Quit()
            return True
        except Exception as e:
            raise ConversionError(f"Excel转PDF失败: {str(e)}")

    def ppt_to_pdf(self, input_file: str, output_file: str) -> bool:
        try:
            powerpoint = win32com.client.Dispatch("Powerpoint.Application")
            presentation = powerpoint.Presentations.Open(input_file)
            presentation.SaveAs(output_file, 32)
            presentation.Close()
            powerpoint.Quit()
            return True
        except Exception as e:
            raise ConversionError(f"PPT转PDF失败: {str(e)}")

    def pdf_to_excel(self, input_file: str, output_file: str) -> bool:
        try:
            pdf_document = fitz.open(input_file)
            total_pages = len(pdf_document)
            all_data = []
            
            for page_num in range(total_pages):
                self.update_progress(int((page_num + 1) * 100 / total_pages))
                page = pdf_document[page_num]
                text = page.get_text("text")
                rows = [row.split() for row in text.split('\n') if row.strip()]
                all_data.extend(rows)
            
            df = pd.DataFrame(all_data)
            df.to_excel(output_file, index=False, header=False)
            return True
        except Exception as e:
            raise ConversionError(f"PDF转Excel失败: {str(e)}")

    def pdf_to_ppt(self, input_file: str, output_file: str) -> bool:
        try:
            prs = Presentation()
            pdf_document = fitz.open(input_file)
            total_pages = len(pdf_document)
            
            for page_num in range(total_pages):
                self.update_progress(int((page_num + 1) * 100 / total_pages))
                page = pdf_document[page_num]
                
                slide = prs.slides.add_slide(prs.slide_layouts[5])
                text = page.get_text()
                if text.strip():
                    txBox = slide.shapes.add_textbox(Inches(1), Inches(1),
                                                   Inches(8), Inches(5))
                    tf = txBox.text_frame
                    tf.text = text
                
                pix = page.get_pixmap()
                img_path = f"temp_slide_{page_num}.png"
                pix.save(img_path)
                
                slide.shapes.add_picture(img_path, Inches(1), Inches(1))
                os.remove(img_path)
            
            prs.save(output_file)
            return True
        except Exception as e:
            raise ConversionError(f"PDF转PPT失败: {str(e)}")

    def word_to_excel(self, input_file: str, output_file: str) -> bool:
        try:
            doc = Document(input_file)
            data = []
            for para in doc.paragraphs:
                if para.text.strip():
                    data.append([para.text])
            
            df = pd.DataFrame(data)
            df.to_excel(output_file, index=False, header=False)
            return True
        except Exception as e:
            raise ConversionError(f"Word转Excel失败: {str(e)}")

    def word_to_ppt(self, input_file: str, output_file: str) -> bool:
        try:
            doc = Document(input_file)
            prs = Presentation()
            
            for para in doc.paragraphs:
                if para.text.strip():
                    slide = prs.slides.add_slide(prs.slide_layouts[5])
                    txBox = slide.shapes.add_textbox(Inches(1), Inches(1),
                                                   Inches(8), Inches(5))
                    tf = txBox.text_frame
                    tf.text = para.text
            
            prs.save(output_file)
            return True
        except Exception as e:
            raise ConversionError(f"Word转PPT失败: {str(e)}")

    def excel_to_word(self, input_file: str, output_file: str) -> bool:
        try:
            df = pd.read_excel(input_file)
            doc = Document()
            
            for _, row in df.iterrows():
                doc.add_paragraph(str(row.to_list()))
            
            doc.save(output_file)
            return True
        except Exception as e:
            raise ConversionError(f"Excel转Word失败: {str(e)}")

    def excel_to_ppt(self, input_file: str, output_file: str) -> bool:
        try:
            df = pd.read_excel(input_file)
            prs = Presentation()
            
            for _, row in df.iterrows():
                slide = prs.slides.add_slide(prs.slide_layouts[5])
                txBox = slide.shapes.add_textbox(Inches(1), Inches(1),
                                               Inches(8), Inches(5))
                tf = txBox.text_frame
                tf.text = str(row.to_list())
            
            prs.save(output_file)
            return True
        except Exception as e:
            raise ConversionError(f"Excel转PPT失败: {str(e)}")

    def ppt_to_word(self, input_file: str, output_file: str) -> bool:
        try:
            prs = Presentation(input_file)
            doc = Document()
            
            for slide in prs.slides:
                for shape in slide.shapes:
                    if hasattr(shape, "text"):
                        doc.add_paragraph(shape.text)
                doc.add_page_break()
            
            doc.save(output_file)
            return True
        except Exception as e:
            raise ConversionError(f"PPT转Word失败: {str(e)}")

    def ppt_to_excel(self, input_file: str, output_file: str) -> bool:
        try:
            prs = Presentation(input_file)
            data = []
            
            for slide in prs.slides:
                slide_text = []
                for shape in slide.shapes:
                    if hasattr(shape, "text"):
                        slide_text.append(shape.text)
                data.append(slide_text)
            
            df = pd.DataFrame(data)
            df.to_excel(output_file, index=False)
            return True
        except Exception as e:
            raise ConversionError(f"PPT转Excel失败: {str(e)}")

    @staticmethod
    def get_output_path(input_file: str, target_format: str) -> str:
        input_path = Path(input_file)
        return str(input_path.parent / f"{input_path.stem}.{target_format.lower()}")
    
    @staticmethod
    def validate_input_file(file_path: str) -> None:
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"找不到文件: {file_path}")
        if not os.path.isfile(file_path):
            raise ValueError(f"不是有效的文件: {file_path}")
    
    def convert(self, input_file: str, target_format: str) -> bool:
        try:
            self.validate_input_file(input_file)
            input_ext = os.path.splitext(input_file)[1].lower()
            target_ext = f".{target_format.lower()}"
            
            conversion_key = (input_ext, target_ext)
            if conversion_key not in self.SUPPORTED_CONVERSIONS:
                raise ConversionError(f"不支持从 {input_ext} 转换到 {target_ext}")
            
            output_file = self.get_output_path(input_file, target_format)
            conversion_method = getattr(self, self.SUPPORTED_CONVERSIONS[conversion_key])
            return conversion_method(input_file, output_file)
            
        except Exception as e:
            raise ConversionError(str(e))

class ConvertThread(QThread):
    finished = pyqtSignal(bool, str)
    progress = pyqtSignal(int, str)
    
    def __init__(self, converter, input_file, target_format):
        super().__init__()
        self.converter = converter
        self.input_file = input_file
        self.target_format = target_format
    
    def run(self):
        try:
            logger.info(f"开始转换: {self.input_file} -> {self.target_format}")
            success = self.converter.convert(self.input_file, self.target_format)
            if success:
                logger.info("转换成功")
                self.finished.emit(True, "")
            else:
                logger.error("转换失败")
                self.finished.emit(False, "转换失败")
        except Exception as e:
            error_msg = str(e)
            logger.error(f"转换错误: {error_msg}")
            self.finished.emit(False, error_msg)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.converter = DocumentConverter()
        self.setup_ui()
        logger.info("程序启动")
        
    def setup_ui(self):
        self.setWindowTitle("文档格式转换器")
        self.setMinimumSize(800, 600)
        
        # 设置主窗口样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #1e1e1e;
            }
            QWidget {
                background-color: #1e1e1e;
                color: #ffffff;
            }
            QLabel {
                color: #ffffff;
            }
        """)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 文件选择区域
        file_layout = QHBoxLayout()
        self.file_label = QLabel("未选择文件")
        self.file_label.setStyleSheet("""
            QLabel {
                padding: 8px;
                border: 1px solid #404040;
                border-radius: 4px;
                background-color: #2d2d2d;
                color: #ffffff;
            }
        """)
        self.select_file_btn = QPushButton("选择文件")
        self.select_file_btn.setStyleSheet("""
            QPushButton {
                padding: 8px 20px;
                background-color: #404040;
                color: #ffffff;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #4a4a4a;
            }
            QPushButton:pressed {
                background-color: #353535;
            }
        """)
        file_layout.addWidget(self.file_label, stretch=1)
        file_layout.addWidget(self.select_file_btn)
        
        # 格式选择区域
        format_layout = QHBoxLayout()
        format_label = QLabel("目标格式:")
        self.format_combo = QComboBox()
        self.format_combo.setStyleSheet("""
            QComboBox {
                padding: 8px;
                border: 1px solid #404040;
                border-radius: 4px;
                background-color: #2d2d2d;
                color: #ffffff;
                min-width: 150px;
            }
            QComboBox::drop-down {
                border: none;
                background-color: #404040;
                width: 30px;
            }
            QComboBox::down-arrow {
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 5px solid #ffffff;
                margin-right: 8px;
            }
            QComboBox:on {
                background-color: #353535;
            }
            QComboBox QAbstractItemView {
                background-color: #2d2d2d;
                color: #ffffff;
                selection-background-color: #404040;
                selection-color: #ffffff;
                border: 1px solid #404040;
            }
        """)
        self.format_combo.addItems(["PDF", "DOCX", "XLSX", "PPTX"])
        format_layout.addWidget(format_label)
        format_layout.addWidget(self.format_combo, stretch=1)
        
        # 进度区域
        progress_layout = QVBoxLayout()
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: 1px solid #404040;
                border-radius: 4px;
                text-align: center;
                background-color: #2d2d2d;
                color: #ffffff;
                height: 25px;
            }
            QProgressBar::chunk {
                background-color: #404040;
                border-radius: 3px;
            }
        """)
        self.progress_bar.setVisible(False)
        
        # 日志显示区域
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setStyleSheet("""
            QTextEdit {
                border: 1px solid #404040;
                border-radius: 4px;
                padding: 8px;
                background-color: #2d2d2d;
                color: #ffffff;
                font-family: Consolas, Monaco, monospace;
                selection-background-color: #404040;
                selection-color: #ffffff;
            }
            QScrollBar:vertical {
                border: none;
                background-color: #2d2d2d;
                width: 12px;
                margin: 0;
            }
            QScrollBar::handle:vertical {
                background-color: #404040;
                min-height: 20px;
                border-radius: 6px;
            }
            QScrollBar::handle:vertical:hover {
                background-color: #4a4a4a;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                height: 0;
                background: none;
            }
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
                background: none;
            }
        """)
        self.log_text.setMinimumHeight(200)
        
        progress_layout.addWidget(self.progress_bar)
        progress_layout.addWidget(self.log_text)
        
        # 转换按钮
        self.convert_btn = QPushButton("开始转换")
        self.convert_btn.setStyleSheet("""
            QPushButton {
                padding: 10px 30px;
                background-color: #404040;
                color: #ffffff;
                border: none;
                border-radius: 4px;
                font-size: 14px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #4a4a4a;
            }
            QPushButton:pressed {
                background-color: #353535;
            }
            QPushButton:disabled {
                background-color: #2d2d2d;
                color: #808080;
            }
        """)
        
        # 添加所有部件到主布局
        main_layout.addLayout(file_layout)
        main_layout.addLayout(format_layout)
        main_layout.addLayout(progress_layout)
        main_layout.addWidget(self.convert_btn)
        main_layout.addStretch()
        
        # 设置布局间距
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)
        
        # 连接信号
        self.select_file_btn.clicked.connect(self.select_file)
        self.convert_btn.clicked.connect(self.convert_file)
        
    def add_log(self, message):
        self.log_text.append(message)
        self.log_text.verticalScrollBar().setValue(
            self.log_text.verticalScrollBar().maximum()
        )
        
    def select_file(self):
        file_filters = [
            "所有支持的文件 (*.pdf *.docx *.xlsx *.pptx)",
            "PDF文件 (*.pdf)",
            "Word文件 (*.docx)",
            "Excel文件 (*.xlsx)",
            "PowerPoint文件 (*.pptx)",
            "所有文件 (*.*)"
        ]
        file_name, _ = QFileDialog.getOpenFileName(
            self,
            "选择文件",
            "",
            ";;".join(file_filters)
        )
        if file_name:
            self.file_label.setText(file_name)
            logger.info(f"选择文件: {file_name}")
            self.add_log(f"选择文件: {file_name}")
            self.update_format_combo(file_name)
            
    def update_format_combo(self, file_name):
        input_ext = os.path.splitext(file_name)[1].lower()
        available_formats = set()
        
        for (in_ext, out_ext) in self.converter.SUPPORTED_CONVERSIONS.keys():
            if in_ext == input_ext:
                available_formats.add(out_ext[1:].upper())
        
        self.format_combo.clear()
        self.format_combo.addItems(sorted(list(available_formats)))
        logger.info(f"可用的输出格式: {', '.join(sorted(list(available_formats)))}")
        self.add_log(f"可用的输出格式: {', '.join(sorted(list(available_formats)))}")
            
    def convert_file(self):
        if not self.file_label.text() or self.file_label.text() == "未选择文件":
            QMessageBox.warning(self, "警告", "请先选择要转换的文件！")
            logger.warning("未选择文件")
            return
            
        self.convert_btn.setEnabled(False)
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        
        input_file = self.file_label.text()
        target_format = self.format_combo.currentText()
        
        logger.info(f"开始转换: {input_file} -> {target_format}")
        self.add_log(f"开始转换: {input_file} -> {target_format}")
        
        self.convert_thread = ConvertThread(
            self.converter,
            input_file,
            target_format
        )
        self.convert_thread.finished.connect(self.on_conversion_finished)
        self.convert_thread.progress.connect(self.update_progress)
        self.convert_thread.start()
        
    def update_progress(self, value, message=""):
        self.progress_bar.setValue(value)
        if message:
            self.add_log(message)
        
    def on_conversion_finished(self, success, error_msg):
        self.convert_btn.setEnabled(True)
        self.progress_bar.setVisible(False)
        
        if success:
            message = "文件转换完成！"
            QMessageBox.information(self, "成功", message)
            logger.info(message)
            self.add_log(message)
        else:
            message = f"转换失败：{error_msg}"
            QMessageBox.critical(self, "错误", message)
            logger.error(message)
            self.add_log(message)

def main():
    app = QApplication(sys.argv)
    
    # 设置应用程序样式
    app.setStyle("Fusion")
    
    window = MainWindow()
    window.show()
    
    sys.exit(app.exec())

if __name__ == "__main__":
    main() 